Skip to content

Add bounds checking on buffer_idx for constant_buffer and constant_data in XNNPACK#18820

Open
lucylq wants to merge 1 commit intomainfrom
security41
Open

Add bounds checking on buffer_idx for constant_buffer and constant_data in XNNPACK#18820
lucylq wants to merge 1 commit intomainfrom
security41

Conversation

@lucylq
Copy link
Copy Markdown
Contributor

@lucylq lucylq commented Apr 10, 2026

Check that buffer_idx is within bounds of flatbuffer_graph->constant_buffer() and flatbuffer_graph->constant_data(), whichever branch is chosen

Authored-with: Claude

@pytorch-bot
Copy link
Copy Markdown

pytorch-bot bot commented Apr 10, 2026

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/18820

Note: Links to docs will display an error until the docs builds have been completed.

❗ 1 Active SEVs

There are 1 currently active SEVs. If your PR is affected, please view them below:

⏳ No Failures, 5 Pending

As of commit 4e15ca5 with merge base 5e8a0df (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Apr 10, 2026
@lucylq lucylq requested a review from Copilot April 10, 2026 18:12
@lucylq lucylq marked this pull request as ready for review April 10, 2026 18:12
@lucylq lucylq requested a review from digantdesai as a code owner April 10, 2026 18:12
@github-actions
Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds bounds validation for buffer_idx when resolving constant tensor data in the XNNPACK flatbuffer path, preventing out-of-range access into constant_buffer / constant_data.

Changes:

  • Add null + bounds checks for flatbuffer_graph->constant_buffer() before indexing.
  • Add null + bounds checks for flatbuffer_graph->constant_data() before calling Get(buffer_idx).
  • Emit error logs and return nullptr on invalid indices.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +188 to +195
if (cb == nullptr || buffer_idx >= cb->size()) {
ET_LOG(
Error,
"Invalid buffer_idx %u for constant_buffer of size %u",
buffer_idx,
cb ? cb->size() : 0);
return nullptr;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning nullptr on an out-of-bounds/nonexistent buffer_idx conflates “non-constant tensor” (buffer_idx==0) with “corrupt/invalid flatbuffer” (buffer_idx!=0 but invalid). Callers like defineTensor pass the returned pointer directly into xnn_define_tensor_value; with a non-zero constant_buffer_idx this can silently create an internal (non-constant) tensor instead of failing fast. Consider changing getConstantDataPtr to return a Result<const uint8_t*> (or add an explicit error flag) so invalid indices propagate as an Error, or at minimum enforce that buffer_idx!=0 implies non-null return and surface an error upstream.

Copilot uses AI. Check for mistakes.
cd ? cd->size() : 0);
return nullptr;
}
ConstantDataOffsetPtr constant_data_offset = cd->Get(buffer_idx);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cd->Get(buffer_idx) can still return nullptr if the vector contains a null offset (possible with corrupted input). constant_data_offset is dereferenced immediately afterward; add a null check for constant_data_offset and handle it as an invalid constant_data entry (log + propagate error).

Suggested change
ConstantDataOffsetPtr constant_data_offset = cd->Get(buffer_idx);
ConstantDataOffsetPtr constant_data_offset = cd->Get(buffer_idx);
if (constant_data_offset == nullptr) {
ET_LOG(
Error,
"Invalid null constant_data entry at buffer_idx %u",
buffer_idx);
return nullptr;
}

Copilot uses AI. Check for mistakes.
@lucylq
Copy link
Copy Markdown
Contributor Author

lucylq commented Apr 10, 2026

@claude review this PR

@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

Claude finished @lucylq's task in 1m 28s —— View job


Review of PR #18820

  • Read the changed files and PR diff
  • Analyze the changes for correctness, security, and style
  • Post review feedback

The fix correctly addresses the core issue: the previous code dereferenced flatbuffer_graph->constant_buffer() and flatbuffer_graph->constant_data() without null or bounds checks, which could cause out-of-bounds reads or null pointer dereferences on malformed flatbuffer input. The added validation is appropriate for a security fix.

Two issues remain that should be addressed:

1. Missing null check after constant_buffer[buffer_idx]->storage()->data() (line 197)

After the bounds check, constant_buffer[buffer_idx] could still be null in a malformed flatbuffer (flatbuffers vectors can contain null entries). Dereferencing it or its ->storage() would crash. This should be guarded:

const auto& constant_buffer = *cb;
auto* buffer_entry = constant_buffer[buffer_idx];
if (buffer_entry == nullptr || buffer_entry->storage() == nullptr) {
  ET_LOG(Error, "Null constant_buffer entry at buffer_idx %u", buffer_idx);
  return nullptr;
}
return buffer_entry->storage()->data();

2. Missing null check after cd->Get(buffer_idx) (line 208)

Similarly, cd->Get(buffer_idx) can return null even with a valid index if the flatbuffer is corrupted. The result is immediately dereferenced on line 209 (constant_data_offset->offset()), which would crash:

ConstantDataOffsetPtr constant_data_offset = cd->Get(buffer_idx);
if (constant_data_offset == nullptr) {
  ET_LOG(Error, "Null constant_data entry at buffer_idx %u", buffer_idx);
  return nullptr;
}

3. Caller impact of returning nullptr on error (minor)

For the defineTensor call site (line 318), buffer_ptr being nullptr is the normal case for non-constant tensors (when buffer_idx == 0). When buffer_idx != 0 but we return nullptr due to an error, this silently creates a non-constant tensor instead of propagating the error. The existing callers at lines 469 and 511 do check for nullptr with ET_CHECK_OR_RETURN_ERROR, so those are fine. The defineTensor path is lower risk since it would produce incorrect results rather than a crash, but worth noting for a future follow-up.

Items 1 and 2 are the higher priority since they leave crash paths open that this PR aims to close. Fix these issues →


Copy link
Copy Markdown
Member

@GregoryComer GregoryComer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update defineTensor to handle the case of a nullptr returned from getConstantDataPtr? It might silently break on malformed PTE.

@lucylq
Copy link
Copy Markdown
Contributor Author

lucylq commented Apr 10, 2026

Can we update defineTensor to handle the case of a nullptr returned from getConstantDataPtr? It might silently break on malformed PTE.

thanks that's a great catch - I'll wrap the return of getConstantDataPtr in Result, so we can differentiate between nullptr and actual error.

Copilot AI review requested due to automatic review settings April 10, 2026 22:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

data_name.c_str(),
static_cast<uint32_t>(buffer.error()));
return nullptr;
return Error::InvalidProgram;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch discards the underlying named_data_map->get_data() error and always returns Error::InvalidProgram. Propagating buffer.error() would preserve the real failure mode (e.g., NotFound/InvalidExternalData) and make error handling/debugging more accurate.

Suggested change
return Error::InvalidProgram;
return buffer.error();

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 14, 2026 20:43
@lucylq
Copy link
Copy Markdown
Contributor Author

lucylq commented Apr 14, 2026

@claude re-review?

@claude
Copy link
Copy Markdown

claude bot commented Apr 14, 2026

Claude finished @lucylq's task in 59s —— View job


Re-review of PR #18820

  • Read the current state of changed files and PR diff
  • Analyze changes for correctness, security, and style
  • Post review feedback

This is a solid revision that addresses all the issues from the first review. The key improvements since the last review:

  1. Result<const uint8_t*> return type -- Properly differentiates between "no constant data" (nullptr success) and "invalid/corrupt flatbuffer" (Error). This was the main gap identified previously.

  2. Null checks on constant_buffer[buffer_idx] and its ->storage() (line 199-203) -- Addresses the crash path on malformed flatbuffers where vector entries could be null.

  3. Null check on cd->Get(buffer_idx) (line 216-220) -- Addresses the null constant_data_offset crash path.

  4. Error propagation instead of silent nullptr (lines 234, 246) -- Both the weights-cache and named-data-map error paths now return data_ptr.error() / buffer.error() instead of nullptr, preserving the specific error code for callers.

  5. All call sites updated -- defineTensor (line 329-339), and the two scale_buffer_idx call sites (lines 484-494, 530-541) all properly unwrap the Result and propagate errors.

One minor observation, not blocking:

  • Line 228: constant_data_offset->named_key() could theoretically be null in a malformed flatbuffer even when IsFieldPresent returns true (the field is present but the offset points to null). Dereferencing with ->str() would crash. This is pre-existing and not introduced by this PR, so it's fine to address separately if at all -- the risk is low since IsFieldPresent guards it.

Overall the PR is clean and correct. The Result-based approach is the right design, and the validation is thorough across both the constant_buffer and constant_data paths. LGTM.


View job

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +210 to +214
buffer_idx < cd->size(),
InvalidProgram,
"buffer_idx %u out of bounds for constant_data of size %zu",
buffer_idx,
cd->size());
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here: the error message uses %zu for cd->size(), but FlatBuffers vector sizes are typically uoffset_t (uint32_t). Please cast to size_t when using %zu (or use a %u-style specifier with an appropriate cast) to avoid varargs type mismatch/UB.

Copilot uses AI. Check for mistakes.
Add bounds checking on buffer_idx in both constant_buffer and
constant_data code paths of getConstantDataPtr to prevent out-of-bounds
vector access from malicious flatbuffer inputs.

Authored-with: Claude
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. security-fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants